#pragma once
#ifndef TM_GPU_FAST_MATCH_HELPER_H_
#define TM_GPU_FAST_MATCH_HELPER_H_

//#include <cuda_runtime.h>
#include <cv.h>

#define MAX_TRANSFORMATIONS 70000000

namespace recognition {

typedef struct FastMatchBoundaries {
	BoundsWithStep<double> tx;
	BoundsWithStep<double> ty;
	BoundsWithStep<double> r;
	BoundsWithStep<double> s;
} FastMatchBoundaries;

typedef struct FastMatchResult {
	FastMatchResult() {
		for (uint i = 0; i < 6; ++i)
			best_config[i] = 0;
		for (uint y = 0; y < 3; ++y)
			for (uint x = 0; x < 3; ++x)
				best_affine[y][x] = 0;
		error = 0;
	}
	double best_config[6];
	double best_affine[3][3];
	double error;
} BestTransformationResult;

typedef struct _cu_eval_distances__global__KernelArgs {
	_cu_eval_distances__global__KernelArgs(uint _r2x, uint _r2y, uint _w2, uint _h2, bool _use_photometric):
		r2x(_r2x), r2y(_r2y), w2(_w2), h2(_h2), use_photometric(_use_photometric) {}

	uint r2x;
	uint r2y;
	uint w2;
	uint h2;
	bool use_photometric;
} _cu_findDistancesKernelArgs;

typedef struct _cu_create_configs__global__KernelArgs {
	_cu_create_configs__global__KernelArgs(FastMatchBoundaries _bounds, uint _ntx_steps, uint _nty_steps, uint _nr_steps, uint _nr2_steps, uint _ns_steps):
		bounds(_bounds), ntx_steps(_ntx_steps), nty_steps(_nty_steps), nr_steps(_nr_steps), nr2_steps(_nr2_steps), ns_steps(_ns_steps) {}

	FastMatchBoundaries bounds;
	uint ntx_steps;
	uint nty_steps;
	uint nr_steps;
	uint nr2_steps;
	uint ns_steps;
} _cu_create_configs__global__KernelArgs;

typedef struct _cu_configs_to_affine__global__KernelArgs {
	_cu_configs_to_affine__global__KernelArgs(int _w1, int _h1, int _w2, int _h2, int _r1x, int _r1y, int _r2x, int _r2y):
		w1(_w1), h1(_h1), w2(_w2), h2(_h2), r1x(_r1x), r1y(_r1y), r2x(_r2x), r2y(_r2y) {}

	int w1, h1, w2, h2;
	int r1x, r1y, r2x, r2y;
} _cu_configs_to_affine__global__KernelArgs;

typedef struct _cu_create_S_random__global__KernelArgs {
	_cu_create_S_random__global__KernelArgs(double _halfstep_tx, double _halfstep_ty, double _halfstep_r, double _halfstep_s) :
		halfstep_tx(_halfstep_tx), halfstep_ty(_halfstep_ty), halfstep_r(_halfstep_r), halfstep_s(_halfstep_s) {}

	double halfstep_tx, halfstep_ty, halfstep_r, halfstep_s;
} _cu_create_S_random__global__KernelArgs;

__global__ void _cu_create_configs__global__(_cu_create_configs__global__KernelArgs *args, uint nconfigs, double* configs);

__global__ void _cu_configs_to_affine__global__(double* configs, double* affines, uint nconfigs,
		_cu_configs_to_affine__global__KernelArgs *args, bool* filter);

__global__ void _cu_filter_configs_and_affines__global__(uint nconfigs, double* configs, double* affines, bool* filter, uint* idxs,
		double* out_configs, double* out_affines);

__global__ void _cu_eval_distances__global__(double* affines, uint naffines, int* xs_centered, int* ys_centered, double* vals_I1, uint npoints,
		double *I, _cu_eval_distances__global__KernelArgs *args, double *distances);

__global__ void _cu_create_S_random__global__(double* Q, uint nQ, uint extension_scale, uint* rand_vec,
		_cu_create_S_random__global__KernelArgs *args, double* S);

template < typename T >
void FillRangeWithStep(T from, T step, uint size, std::vector<T>& v);

template < typename T >
T mean(std::vector<T>& v, uint from, uint to);

// max_rand inclusive
void FillWithRandoms(uint size, uint max_rand, uint from, std::vector<uint>& v);
void fill_xsys(uint w, uint h, std::vector<int>& xs, std::vector<int>& ys);
void GetRandomPoints(uint npoints, uint w, uint h, std::vector<int>& xs, std::vector<int>& ys);

void ReportNumberOfCreatedConfigs(log4cxx::LoggerPtr logger, uint level, uint nconfigs);
void ReportNumberOfCreatedAffines(log4cxx::LoggerPtr logger, uint naffines, double time);
void ReportAffinesEvaluationTime(log4cxx::LoggerPtr logger, double time);
void ReportLevelTime(log4cxx::LoggerPtr logger, uint level, double time);
void ReportFastMatchTime(log4cxx::LoggerPtr logger, double time);
void ReportFastMatchResult(log4cxx::LoggerPtr logger, FastMatchResult res);

} // namespace recognition

#endif	// TM_GPU_FAST_MATCH_HELPER_H_
